#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <poll.h>
#include "sock.hpp"

namespace poll_ns
{
    static const uint16_t defaultport = 8080;

    class PollServer
    {
        static const int num = 2048;
        static const int defaultfd = -1;
        using func_t = std::function<std::string(const string &)>;

    public:
        PollServer(const func_t &func, const uint16_t &port = defaultport)
            : _listensock(-1), _port(port), _rfds(nullptr), _func(func)
        {
        }

        void init()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);

            _rfds = new struct pollfd[num];

            // 进行初始化清空
            for (int i = 0; i < num; i++)
            {
                Reset(i);
            }

            // 设置监听套接字
            _rfds[0].fd = _listensock;
            _rfds[0].events = POLLIN;
        }

        void start()
        {
            int time_out = -1;
            for (;;)
            {
                // int n = poll(_rfds, num, 1000);              // 每1000ms返回一次
                int n = poll(_rfds, num, time_out); // 阻塞
                // int n = poll(_rfds, num, 0);                 // 非阻塞
                switch (n)
                {
                case 0:
                    // 超时了，在阻塞的时间段内没有事件就绪，变为非阻塞
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    logMessage(WARNING, "errno: %d, err_msg: %s", errno, strerror(errno));
                    break;
                default:
                    // 事件就绪，目前只有一个监听事件
                    logMessage(NORMAL, "have a event!");
                    HandlerReadEvent();
                    break;
                }
            }
        }

        void Reset(int pos)
        {
            _rfds[pos].fd = defaultfd;
            _rfds[pos].events = 0;
            _rfds[pos].revents = defaultfd;
        }

        void HandlerReadEvent()
        {
            if (_rfds[0].fd == _listensock && (_rfds[0].revents & POLLIN))
                Accepter();
            for (int i = 1; i < num; i++)
            {
                if (_rfds[i].fd == defaultfd)
                    continue;

                if (!(_rfds[i].events & POLLIN)) // 这里判断再严谨一点，必须是曾经设过POLLIN的才处理
                    continue;

                if (_rfds[i].revents & POLLIN)
                    Recver(i);
            }
        }

        void Accepter()
        {
            // if (_fdarray[0] == _listensock && FD_ISSET(_listensock, &rfds))
            {
                // 走到这，一定是select告诉我listensock有事件就绪了
                std::string clientip;
                uint16_t clientport;

                // 走到这，accept就不会阻塞了，因为此时一定有事件就绪了，轮到你accept干活了
                int sock = Sock::Accept(_listensock, &clientip, &clientport);
                if (sock < 0)
                    return;

                logMessage(NORMAL, "accept success! [%s: %d]", clientip.c_str(), clientport);

                // 走到这，就能直接recv/read吗，不行！如果read/recv了，这下又成了阻塞式IO了
                // 所以也是要交给select去检测事件是否就绪的

                // 将新的sockfd托管给select，本质就是添加到_fdarray数组中
                int i = 0;
                for (; i < num; i++)
                {
                    // 找到一个非法的位置，然后进行插入
                    if (_rfds[i].fd != defaultfd)
                        continue;
                    else
                        break;
                }

                if (i == num)
                {
                    // 说明服务器承载能力到达极限
                    logMessage(WARNING, "server is full, please wait!");
                    close(sock);
                }
                else
                {
                    // 走到这，说明有了空位置，可以让poll帮我们检测
                    _rfds[i].fd = sock;
                    _rfds[i].events = POLLIN;
                    _rfds[i].revents = 0;
                }
            }
            PrintFdlist();
        }

        void PrintFdlist()
        {
            std::cout << "fd_list: ";
            for (int i = 0; i < num; i++)
            {
                if (_rfds[i].fd != defaultfd)
                    std::cout << _rfds[i].fd << " ";
            }
            std::cout << std::endl;
        }

        void Recver(int pos)
        {
            // 1. 读取客户端request
            // 走到这，read还会不会阻塞住呢？ 不会
            char buffer[1024];
            ssize_t s = recv(_rfds[pos].fd, buffer, sizeof(buffer) - 1, 0);

            // 这里的read是有问题的，你怎么保证读到的就是完整的报文呢？所以这里要打while循环去读(参考之前网络版本计算器的服务器处理字节流数据问题)
            // 但是你怎么保证第二次第三次读取不会被阻塞住呢？
            // 所以我们这里先不管了，今天只是做测试，后面讲epoll再来处理
            if (s > 0)
            {
                buffer[s] = 0;
                logMessage(NORMAL, "client# %s", buffer);
            }
            else if (s == 0)
            {
                close(_rfds[pos].fd);
                Reset(pos);
                logMessage(NORMAL, "client quit!");
                return;
            }
            else
            {
                close(_rfds[pos].fd);
                Reset(pos);
                logMessage(ERROR, "client quit! err_msg: %s", strerror(errno));
                return;
            }

            // 2. 读到客户端request之后，进行处理
            std::string resp = _func(buffer);

            // write 这里也是有问题的，因为我们没有添加维护写事件的文件描述符集，后面epoll都会解决，select这里处理只会让代码变得更复杂
            // 3. 这里简单回显客户端发的消息
            send(_rfds[pos].fd, resp.c_str(), resp.size(), 0);
        }

        ~PollServer()
        {
            if (_listensock > 0)
                close(_listensock);
            if (_rfds)
                delete[] _rfds;
        }

    private:
        int _listensock;
        uint16_t _port;
        struct pollfd *_rfds;
        func_t _func;
    };

} // namespace selectServer_ns
